#include "common_lib/socket_server.h"

using namespace Socket;

SocketServer::SocketServer(const std::string ipaddress, const unsigned short port, const unsigned short num_threads) noexcept
: m_ios_acceptors_(boost::make_shared<boost::asio::io_service>()),
  m_ios_work_acceptors_(boost::make_shared<boost::asio::io_service::work>(*m_ios_acceptors_)),
  m_ios_executors_{boost::make_shared<boost::asio::io_service>()},
  m_ios_work_executors_{boost::make_shared<boost::asio::io_service::work>(*m_ios_executors_)},
  m_endpoint_{boost::asio::ip::address::from_string(ipaddress), port},
  m_acceptor_(*m_ios_acceptors_, m_endpoint_),
  ptr_session_(boost::make_shared<SocketSession>(m_ios_executors_, this)),
  m_signals_{*m_ios_acceptors_, SIGINT, SIGTERM}
{
    printf("starting %s, %d\n", ipaddress.c_str(), port);
    m_signals_.async_wait(boost::bind(&SocketServer::Stop, this));
    for (unsigned int i = 0; i < num_threads; i++)
    {
        m_executors_thread_group_.create_thread(boost::bind(&SocketServer::WorkerThreadCallback, this, m_ios_executors_));
    }
    m_acceptor_.async_accept(ptr_session_->GetSocket(),
                             boost::bind(&SocketServer::AcceptHandler, this, ptr_session_, boost::asio::placeholders::error));

    printf("started, press CTRL+C to quit\n");
}

SocketServer::~SocketServer() noexcept
{
    Stop();
}

bool SocketServer::Start()
{
    boost::system::error_code ec;
    m_ios_acceptors_->run(ec);
    if (ec)
    {
        return false;
    }
    return true;
}

void SocketServer::Stop()  noexcept
{
    if (!m_ios_acceptors_->stopped())
    {
        m_ios_acceptors_->stop();
    }

    if (!m_ios_executors_->stopped())
    {
        m_ios_executors_->stop();
        m_executors_thread_group_.interrupt_all();
        m_executors_thread_group_.join_all();
    }
}

void SocketServer::WorkerThreadCallback(boost::shared_ptr<boost::asio::io_service> ios) noexcept
{
    boost::system::error_code ec;
    ios->run(ec);
    if (ec)
    {
        // spdlog::error("callback error: {}", ec.message());
    }
}

void SocketServer::AcceptHandler(boost::shared_ptr<SocketSession> session, const boost::system::error_code &ec) noexcept
{
    if (!ec)
    {
        const std::string client_ip = session->GetSocket().remote_endpoint().address().to_string();
        const uint16_t    client_port = session->GetSocket().remote_endpoint().port();

        printf("AcceptHandler() --> ip: %s, port: %d\n", client_ip.c_str(), client_port);

        m_ios_executors_->post(boost::bind(&SocketSession::Start, session));

        boost::shared_ptr<SocketSession> tmp = session;

        bumtmp.insert(std::make_pair(client_port, tmp));

        ptr_session_.reset();
        ptr_session_ = boost::make_shared<SocketSession>(m_ios_executors_);
        m_acceptor_.async_accept(session->GetSocket(), boost::bind(&SocketServer::AcceptHandler, this, ptr_session_, boost::asio::placeholders::error));

        for (auto p : bumtmp)
        {
            printf("port: %d, cnt: %ld\n", p.first, p.second.use_count());
        }
    }
}

void SocketServer::ClientStopCallback(unsigned short port)
{
    if (bumtmp.find(port) != bumtmp.end())
    {
        bumtmp.erase(port);
    }
}

void SocketServer::ClientReadCallback(std::string str_read)
{

}

void SocketServer::SeverWrite(std::string &str_write, unsigned short &port)
{
    if (bumtmp.find(port) == bumtmp.end())  
        return;

    if (bumtmp[port]->Write(str_write))
    {
    }
}

// #include <ros/ros.h>
// int main(int argc, char * argv[])
// {
//     ros::init(argc, argv, "socket_server");
//     std::string ipaddress("127.0.0.1");
//     unsigned short port = 9900;

//     Socket::SocketServer server(ipaddress, port, 10);
//     if (!server.Start())
//     {
//         printf("EXIT_FAILURE\n");
//     }

//     // ros::spin();
//     ros::shutdown();
//     return 0;
// }